[id].vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <template>
  2. <div class="max-w-screen-xl content my-0 mx-auto text-size-base overflow-hidden"
  3. :class="!detail ? 'flex flex-column justify-center items-center h-full' : ''">
  4. <div v-if="detail">
  5. <div class="breadcrumb my-[30px] pb-[20px] bb-1px_#EBEBEB" role="navigation">
  6. <n-breadcrumb separator=">" v-if="breadcrumb">
  7. <template v-for="(bread, index) in breadcrumb" :key="index">
  8. <n-breadcrumb-item :clickable="false"> {{ bread.title }}</n-breadcrumb-item>
  9. </template>
  10. </n-breadcrumb>
  11. </div>
  12. <div v-if="currentCate">
  13. <n-h2 class="mb-10"> {{ currentCate.title }}</n-h2>
  14. </div>
  15. <div class="w-full flex flex-row flex-nowrap">
  16. <div class="flex-basis-[240px] flex-grow-0 flex-shrink-0 br-1px_#EBEBEB">
  17. <n-tree class="left-tree" v-if="mainCategories" block-line :data="mainCategories.children"
  18. :default-expanded-keys="defaultExpandedKeys" :node-props="nodeProps" key-field="id" label-field="title"
  19. children-field="children" selectable />
  20. </div>
  21. <div class="flex-1 w-[calc(100%-80px)] px-[40px] mb-[120px] overflow-hidden">
  22. <div class="bb-1px_#EBEBEB color-[#999999]">
  23. <n-h1 class="font-700"> {{ detail.title }}</n-h1>
  24. <span class="flex flex-row gap-col-6 pb-[15px]">
  25. <p>{{ $t('publish') }} {{ dayjs(detail.createTime).format('YYYY-MM-DD') }}</p>
  26. <p>{{ $t('read') }} ( {{ detail.readCount }} )</p>
  27. </span>
  28. </div>
  29. <div class="w-full content-html" v-html="detail.content"></div>
  30. <div class="w-full flex justify-between pt-6 bt-1px_#EBEBEB color-[#999898]">
  31. <div class="prev flex flex-col">
  32. <span class="flex-1 text-align-left">
  33. {{ $t('prev_article') }} </span>
  34. <span class="flex-1 articleNear" v-if="articleNear[0]" @click="handleToArticle(articleNear[0])">
  35. {{ articleNear[0].title }}
  36. </span>
  37. <span v-else>
  38. 暂没数据
  39. </span>
  40. </div>
  41. <div class="next flex flex-col ">
  42. <span class="flex-1 text-align-right">
  43. {{ $t('next_article') }}
  44. </span>
  45. <span class="flex-1 articleNear" v-if="articleNear[1]" @click="handleToArticle(articleNear[1])">
  46. {{ articleNear[1].title }}
  47. </span>
  48. <span v-else>
  49. 暂没数据
  50. </span>
  51. </div>
  52. </div>
  53. </div>
  54. <div class="flex-basis-[240px] flex-grow-0 flex-shrink-0">
  55. <div class="min-h-[200px] bg-[#F5F9FF] b-r-[8px] p-[20px]">
  56. <n-h4 class="font-600 ext-size-base">{{ $t('main_content') }}</n-h4>
  57. <n-anchor :show-rail="false">
  58. <n-anchor-link class="text-size-base color-[#999999]" v-for="(item, index) in mainContents" :key="index"
  59. :title="item.text" :href="`#my-section-${index + 1}`">
  60. </n-anchor-link>
  61. </n-anchor>
  62. </div>
  63. </div>
  64. </div>
  65. </div>
  66. <div v-else>
  67. <n-empty :description="$t('no_data')">
  68. <template #extra>
  69. {{ $t('no_data_article') }}
  70. <n-button size="small" type="tertiary" @click="router.push('/')">
  71. {{ $t('go_home') }}
  72. </n-button>
  73. </template>
  74. </n-empty>
  75. </div>
  76. </div>
  77. </template>
  78. <route>
  79. {
  80. name: "showdoc",
  81. meta: {
  82. layout: "base"
  83. }
  84. }
  85. </route>
  86. <script setup lang="ts">
  87. import {
  88. type ArticleDetailType,
  89. type ArticleDetailMenuType,
  90. type CategoryItem,
  91. getArticleDetail,
  92. getArticleCount,
  93. getCategoryTree,
  94. // getArticlesByCateId,
  95. getNearArticles,
  96. } from '@/api'
  97. import { htmlToTree, createAnchorNames, dayjs, findNodeById, findBreadcrumbPath, type TreeNode } from '@/utils'
  98. import { NH1, NH2, NH4, NEmpty, NButton, NBreadcrumb, NBreadcrumbItem, NTree, NAnchor, NAnchorLink } from 'naive-ui'
  99. import type { TreeOption } from 'naive-ui'
  100. const route = useRoute()
  101. const router = useRouter()
  102. const params = route.params as {
  103. id?: number
  104. }
  105. const breadcrumb = ref<CategoryItem[]>([])
  106. console.log('route', route)
  107. const detail = ref<ArticleDetailType | undefined>()
  108. const mainContents = ref<ArticleDetailMenuType[]>([])
  109. const currentCate = ref<CategoryItem | undefined>()
  110. const mainCategories = ref<CategoryItem[]>([])
  111. const defaultExpandedKeys = ref<number[]>([])
  112. const articleNear = ref<ArticleDetailType[]>([])
  113. const nodeProps = ({ option }: { option: TreeOption }) => {
  114. return {
  115. async onClick() {
  116. console.log('option', option.id)
  117. router.replace(`/showcate/${option.id}`)
  118. setTimeout(() => {
  119. location.reload()
  120. }, 50)
  121. },
  122. }
  123. }
  124. onMounted(async () => {
  125. setTimeout(() => {
  126. const html = document.querySelector('.content-html')
  127. if (html) {
  128. createAnchorNames(html)
  129. }
  130. }, 1000)
  131. if (params.id) {
  132. await getArticleCount(+params.id)
  133. }
  134. })
  135. watchEffect(() => {
  136. if (params.id) {
  137. getArticleDetail(+params.id).then(async (data) => {
  138. if (data.data) {
  139. detail.value = data.data
  140. document.title = detail.value.title
  141. mainContents.value = htmlToTree(detail.value.content)
  142. if (detail.value.categoryId) {
  143. const res = await getCategoryTree(detail.value.categoryId)
  144. if (res.data) {
  145. mainCategories.value = res.data as CategoryItem[]
  146. if (mainCategories.value) {
  147. currentCate.value = findNodeById(
  148. [mainCategories.value] as unknown as TreeNode[],
  149. detail.value.categoryId,
  150. ) as unknown as CategoryItem
  151. defaultExpandedKeys.value = [currentCate.value.parentId]
  152. breadcrumb.value = findBreadcrumbPath([mainCategories.value] as unknown as TreeNode[], detail.value.categoryId)
  153. console.log('breadcrumb', [mainCategories.value], breadcrumb.value)
  154. }
  155. }
  156. }
  157. }
  158. })
  159. getNearArticles(+params.id).then(res => {
  160. if (res.data) {
  161. articleNear.value = res.data
  162. }
  163. })
  164. }
  165. })
  166. const handleToArticle = (article: ArticleDetailType) => {
  167. console.log('article', article)
  168. router.replace(`/showdoc/${article.id}`)
  169. setTimeout(() => {
  170. location.reload()
  171. }, 500)
  172. }
  173. </script>
  174. <style lang="scss" scoped>
  175. :deep(.content-html img) {
  176. width: 100% !important;
  177. height: auto;
  178. }
  179. :deep(.left-tree) {
  180. --n-node-content-height: 40px !important;
  181. //--n-node-color-hover: rgba(6,97,201,0.06) !important;
  182. //border-right: 1px solid #e5e7eb;
  183. .n-tree-node-wrapper {
  184. width: 240px;
  185. font-size: 16px;
  186. }
  187. }
  188. .articleNear {
  189. &:hover {
  190. cursor: pointer;
  191. color: #5a5a5a;
  192. }
  193. }
  194. </style>